#pragma once

// Note the order!
#if defined(__clang__)
    #define COMPILER_IS_CLANG true
    #define COMPILER_IS_GCC   false
    #define COMPILER_IS_MSVC  false
#elif defined(__GNUC__)
    #define COMPILER_IS_CLANG false
    #define COMPILER_IS_GCC   true
    #define COMPILER_IS_MSVC  false
#elif defined(_MSC_VER)
    #define COMPILER_IS_CLANG false
    #define COMPILER_IS_GCC   false
    #define COMPILER_IS_MSVC  true
#endif

#if COMPILER_IS_CLANG || COMPILER_IS_GCC
    #define PRETTY_FUNC_NAME __PRETTY_FUNCTION__
    #define DLL_PUBLIC       extern "C" __attribute__((visibility("default")))
#elif COMPILER_IS_MSVC
    #define PRETTY_FUNC_NAME __FUNCTION__
    #ifdef BUILD_DLL
        #define DLL_PUBLIC extern "C" __declspec(dllexport)
    #elif
        #define DLL_PUBLIC extern "C" __declspec(dllimport)
    #endif
#endif

#ifndef NDEBUG
    #define DEBUG
#endif

#define UNREACHABLE() __builtin_unreachable()

#define IF_CONSTEVAL \
    if               \
    consteval

#define IS_NOEXCEPT(...) \
    {                    \
        __VA_ARGS__      \
    }                    \
    noexcept

#define IS_CONVERTIBLE_TO(type, ...) \
    {                                \
        __VA_ARGS__                  \
    } -> ::std::convertible_to<type>

#define IS_IDENTICAL_TO(type, ...) \
    {                              \
        __VA_ARGS__                \
    } -> ::stx::IsIdentical<type>

#define IS_SAME_AS(type, ...) \
    {                         \
        __VA_ARGS__           \
    } -> ::std::same_as<type>

#define MACRO_TO_STRING__(...)   #__VA_ARGS__
#define MACRO_TO_STRING(...)     MACRO_TO_STRING__(__VA_ARGS__)
#define MACRO_CONCAT_IMP__(a, b) a##b
#define MACRO_CONCAT(a, b)       MACRO_CONCAT_IMP__(a, b)

#define FORWARD(...) ::std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__)

#define DECAY_COPY(...) \
    ::std::decay_t<decltype(__VA_ARGS__)>(FORWARD(__VA_ARGS__))

#define CREF(var)             &var = ::std::as_const(var)
#define REF_ARGS(args)        &... args = args
#define CREF_ARGS(args)       &... args = ::std::as_const(args)
#define DECAY_COPY_ARG(var)   var = DECAY_COPY(var)
#define DECAY_COPY_ARGS(args) ... args = DECAY_COPY(args)
#define MOVE(var)             var = ::std::move(var)
#define MOVE_ARGS(args)       ... args = ::std::move(args)

// Never use EXPR() to enclose a fold expression; or clangd will go mad.
#define EXPR(...) (__VA_ARGS__)

#define GLOBAL_VAR(name, ...)         \
    inline auto& name()               \
    {                                 \
        static auto ls = __VA_ARGS__; \
        return ls;                    \
    }                                 \
                                      \
    static_assert(true)

#define CONSTINIT_GLOBAL_VAR(name, ...)         \
    inline auto& name()                         \
    {                                           \
        constinit static auto ls = __VA_ARGS__; \
        return ls;                              \
    }                                           \
                                                \
    static_assert(true)

#define GLOBAL_VAR_CONST(name, ...)         \
    inline auto const& name()               \
    {                                       \
        static auto const ls = __VA_ARGS__; \
        return ls;                          \
    }                                       \
                                            \
    static_assert(true)

#define CONSTINIT_GLOBAL_VAR_CONST(name, ...)         \
    inline auto const& name()                         \
    {                                                 \
        constinit static auto const ls = __VA_ARGS__; \
        return ls;                                    \
    }                                                 \
                                                      \
    static_assert(true)

#define TLS_VAR(name, ...)                  \
    inline auto& name()                     \
    {                                       \
        thread_local auto ls = __VA_ARGS__; \
        return ls;                          \
    }                                       \
                                            \
    static_assert(true)

#define CONSTINIT_TLS_VAR(name, ...)                  \
    inline auto& name()                               \
    {                                                 \
        constinit thread_local auto ls = __VA_ARGS__; \
        return ls;                                    \
    }                                                 \
                                                      \
    static_assert(true)

#define TLS_VAR_CONST(name, ...)                  \
    inline auto const& name()                     \
    {                                             \
        thread_local auto const ls = __VA_ARGS__; \
        return ls;                                \
    }                                             \
                                                  \
    static_assert(true)

#define CONSTINIT_TLS_VAR_CONST(name, ...)                  \
    inline auto const& name()                               \
    {                                                       \
        constinit thread_local auto const ls = __VA_ARGS__; \
        return ls;                                          \
    }                                                       \
                                                            \
    static_assert(true)

#define NON_COPYABLE(cls_name)                     \
    cls_name(cls_name const&)            = delete; \
    cls_name& operator=(cls_name const&) = delete

#define NON_MOVABLE(cls_name)                          \
    cls_name(cls_name&&) noexcept            = delete; \
    cls_name& operator=(cls_name&&) noexcept = delete

/*
class X
{
public:
    ...
    X(X const&); // Implement it!
	DEFINE_COPY_SEMANTICS(X, X const&);

    X(Y const&); // Implement it!
	DEFINE_COPY_SEMANTICS(X, Y const&);
    ...
};
*/
#define DEFINE_COPY_SEMANTICS(cls_name, ...)                        \
    cls_name& operator=(__VA_ARGS__ other) noexcept(                \
        ::std::is_nothrow_copy_constructible_v<cls_name>)           \
    {                                                               \
        if (static_cast<void const*>(this) !=                       \
            static_cast<void const*>(&other))                       \
        {                                                           \
            if constexpr (::std::is_move_constructible_v<cls_name>) \
            {                                                       \
                *this = cls_name(FORWARD(other));                   \
            }                                                       \
            else                                                    \
            {                                                       \
                this->~cls_name();                                  \
                new (this) cls_name(FORWARD(other));                \
            }                                                       \
        }                                                           \
                                                                    \
        return *this;                                               \
    }                                                               \
    static_assert(true)

/*
class X
{
public:
    ...
    DEFINE_MOVE_SEMANTICS(X,
    {
		::close(this->fd_);
		this->fd_   = other.fd_;
		other.fd_   = C_UNDEFINED;		
        this->data_ = std::exchange(other.data_, {});
    });
    ...
};
*/
#define DEFINE_MOVE_SEMANTICS(cls_name, ...)                           \
    cls_name(cls_name&& other) noexcept                                \
    {                                                                  \
        this->move___(other);                                          \
    }                                                                  \
                                                                       \
    cls_name& operator=(cls_name&& other) noexcept                     \
    {                                                                  \
        if (this != &other)                                            \
        {                                                              \
            this->move___(other);                                      \
        }                                                              \
                                                                       \
        return *this;                                                  \
    }                                                                  \
                                                                       \
    template<::stx::IsIdentical<cls_name> Self_>                       \
    requires EXPR(!::stx::IsConst<::std::remove_reference_t<Self_&&>>) \
    void move___(Self_&& other) noexcept                               \
    {                                                                  \
        if (this != &other)                                            \
        {                                                              \
            __VA_ARGS__                                                \
        }                                                              \
    }                                                                  \
                                                                       \
    static_assert(true)

#define FOR(idx, ...)          \
    if (auto idx = -1uz; true) \
        for (__VA_ARGS__)      \
            if (++idx, true)

#define LOOP(idx, ...)                                                         \
    for (auto MACRO_CONCAT(size___, __LINE__) = __VA_ARGS__,                   \
                                    idx =                                      \
                                        ::stx::NoCvr<decltype(__VA_ARGS__)>{}; \
         idx < MACRO_CONCAT(size___, __LINE__);                                \
         ++idx)

#define LOOP_N(...) LOOP (MACRO_CONCAT(unused_id___, __LINE__), __VA_ARGS__)

// #include <compare>

// struct IntWrapper
// {
//     int value;
//
//     constexpr IntWrapper(int const value) : value(value)
//     {}

//     [[nodiscard]] constexpr auto operator<=>(IntWrapper const&) const
//         noexcept = default;
//     [[nodiscard]] constexpr bool operator==(IntWrapper const&) const
//         noexcept = default;
// };
#define SPACESHIP_OPERATORS(cls_name)                                          \
    [[nodiscard]] constexpr auto operator<=>(cls_name const&) const noexcept = \
        default;                                                               \
    [[nodiscard]] constexpr bool operator==(cls_name const&) const noexcept =  \
        default

#define SPACESHIP_OPERATORS_NO_CONSTEXPR(cls_name)                            \
    [[nodiscard]] auto operator<=>(cls_name const&) const noexcept = default; \
    [[nodiscard]] bool operator==(cls_name const&) const noexcept  = default

#define EQUALITY_OPERATOR(cls_name)                                           \
    [[nodiscard]] constexpr bool operator==(cls_name const&) const noexcept = \
        default

#define EQUALITY_OPERATOR_NO_CONSTEXPR(cls_name) \
    [[nodiscard]] bool operator==(cls_name const&) const noexcept = default

#define DEFINE_SAFE_NUMBER(cls_name, underlying_type, default_value)       \
    struct cls_name final                                                  \
    {                                                                      \
        using SafeNumberTag_ = void;                                       \
        using value_type     = underlying_type;                            \
                                                                           \
        underlying_type value = default_value;                             \
                                                                           \
        static_assert(::stx::IsArithmetic<underlying_type>);               \
        static_assert(::stx::IsRaw<underlying_type>);                      \
                                                                           \
        constexpr cls_name() noexcept = default;                           \
        explicit constexpr cls_name(underlying_type const v) noexcept      \
            : value(v)                                                     \
        {}                                                                 \
                                                                           \
        SPACESHIP_OPERATORS(cls_name);                                     \
                                                                           \
        [[nodiscard]] constexpr operator underlying_type() const noexcept  \
        {                                                                  \
            return value;                                                  \
        }                                                                  \
                                                                           \
        [[nodiscard]] constexpr ::std::size_t getHashCode() const noexcept \
        {                                                                  \
            return static_cast<::std::size_t>(this->value);                \
        }                                                                  \
    }

#define DEFINE_SAFE_BOOL(cls_name) DEFINE_SAFE_NUMBER(cls_name, bool, true)

#define DEFINE_BASIC_SAFE_STRING(cls_name, ex_std_string)                     \
    class cls_name final : public ex_std_string                               \
    {                                                                         \
        static_assert(::stx::IsRaw<ex_std_string>);                           \
        static_assert(::stx::IsExStdString<ex_std_string>);                   \
        using Base_ = ex_std_string;                                          \
                                                                              \
    public:                                                                   \
        SPACESHIP_OPERATORS_NO_CONSTEXPR(cls_name);                           \
                                                                              \
        using Base_::Base_;                                                   \
                                                                              \
        explicit cls_name(Base_ const& other) : Base_(other)                  \
        {}                                                                    \
                                                                              \
        explicit cls_name(Base_&& other) noexcept : Base_(::std::move(other)) \
        {}                                                                    \
                                                                              \
        [[nodiscard]] ::std::size_t getHashCode() const noexcept              \
        {                                                                     \
            return ::std::hash<ex_std_string>{}(*this);                       \
        }                                                                     \
    }

#define DEFINE_SAFE_STRING(cls_name) \
    DEFINE_BASIC_SAFE_STRING(cls_name, ::std::string)

#define DEFINE_SAFE_DURATION(cls_name, default_value)        \
    struct cls_name final : public ::stx::Duration           \
    {                                                        \
        using Base_ = ::stx::Duration;                       \
                                                             \
        constexpr cls_name() noexcept : Base_(default_value) \
        {}                                                   \
                                                             \
        explicit constexpr cls_name(                         \
            ::stx::IsDurationLike auto const dur) noexcept   \
            : Base_(dur)                                     \
        {}                                                   \
                                                             \
        SPACESHIP_OPERATORS(cls_name);                       \
    }

// Overloaded Macros:

// #define F0()
// #define F1(a)
// #define F2(a, b)
// #define F3(a, b, c)

// #define F__(_1_, _2_, _3_, real_macro_name, ...) real_macro_name

// #define F(...) F__(__VA_ARGS__ __VA_OPT__(, ) F3, F2, F1, F0)(__VA_ARGS__)

// F()        -> F0()
// F(a)       -> F1(a)
// F(a, b)    -> F2(a, b)
// F(a, b, c) -> F3(a, b, c)
#define GET_ARG_COUNT__(_1_,   \
                        _2_,   \
                        _3_,   \
                        _4_,   \
                        _5_,   \
                        _6_,   \
                        _7_,   \
                        _8_,   \
                        _9_,   \
                        _10_,  \
                        _11_,  \
                        _12_,  \
                        _13_,  \
                        _14_,  \
                        _15_,  \
                        _16_,  \
                        _17_,  \
                        _18_,  \
                        _19_,  \
                        _20_,  \
                        _21_,  \
                        _22_,  \
                        _23_,  \
                        _24_,  \
                        _25_,  \
                        _26_,  \
                        _27_,  \
                        _28_,  \
                        _29_,  \
                        _30_,  \
                        _31_,  \
                        _32_,  \
                        _33_,  \
                        _34_,  \
                        _35_,  \
                        _36_,  \
                        _37_,  \
                        _38,   \
                        _39,   \
                        _40_,  \
                        _41_,  \
                        _42_,  \
                        _43_,  \
                        _44_,  \
                        _45_,  \
                        _46_,  \
                        _47_,  \
                        _48_,  \
                        _49_,  \
                        _50_,  \
                        _51_,  \
                        _52_,  \
                        _53_,  \
                        _54_,  \
                        _55_,  \
                        _56_,  \
                        _57_,  \
                        _58_,  \
                        _59_,  \
                        _60_,  \
                        _61_,  \
                        _62_,  \
                        _63_,  \
                        _64_,  \
                        _65_,  \
                        _66_,  \
                        _67_,  \
                        _68_,  \
                        _69_,  \
                        _70_,  \
                        count, \
                        ...)   \
    count

#define GET_ARG_COUNT(...)                         \
    GET_ARG_COUNT__(__VA_ARGS__ __VA_OPT__(, ) 70, \
                    69,                            \
                    68,                            \
                    67,                            \
                    66,                            \
                    65,                            \
                    64,                            \
                    63,                            \
                    62,                            \
                    61,                            \
                    60,                            \
                    59,                            \
                    58,                            \
                    57,                            \
                    56,                            \
                    55,                            \
                    54,                            \
                    53,                            \
                    52,                            \
                    51,                            \
                    50,                            \
                    49,                            \
                    48,                            \
                    47,                            \
                    46,                            \
                    45,                            \
                    44,                            \
                    43,                            \
                    42,                            \
                    41,                            \
                    40,                            \
                    39,                            \
                    38,                            \
                    37,                            \
                    36,                            \
                    35,                            \
                    34,                            \
                    33,                            \
                    32,                            \
                    31,                            \
                    30,                            \
                    29,                            \
                    28,                            \
                    27,                            \
                    26,                            \
                    25,                            \
                    24,                            \
                    23,                            \
                    22,                            \
                    21,                            \
                    20,                            \
                    19,                            \
                    18,                            \
                    17,                            \
                    16,                            \
                    15,                            \
                    14,                            \
                    13,                            \
                    12,                            \
                    11,                            \
                    10,                            \
                    9,                             \
                    8,                             \
                    7,                             \
                    6,                             \
                    5,                             \
                    4,                             \
                    3,                             \
                    2,                             \
                    1,                             \
                    0)

#ifdef DEBUG
    #define ASSERT(...) CHECK(__VA_ARGS__)
#else
    #define ASSERT(...)
#endif

#define DIAGNOSE_TYPE(...) ::stx::unused(::stx::DiagnoseType__<__VA_ARGS__>{})

#define ON_SCOPE_EXIT(...)                                       \
    [[maybe_unused]] auto MACRO_CONCAT(unique_id___, __LINE__) = \
        ::stx::ScopeExitExecutor__([&] { __VA_ARGS__; })

#define LOCK(...)                                                \
    [[maybe_unused]] auto MACRO_CONCAT(unique_id___, __LINE__) = \
        decltype(::stx::getLockGuardType__(__VA_ARGS__))(__VA_ARGS__)

#define LOG__(level, ...)                                                     \
    do                                                                        \
    {                                                                         \
        if (!::stx::gLoggerIsEnabled__())                                     \
        {                                                                     \
            break;                                                            \
        }                                                                     \
                                                                              \
        if constexpr ("TRACE"sv == #level)                                    \
        {                                                                     \
            if (::stx::gLogLevel() > 0)                                       \
            {                                                                 \
                break;                                                        \
            }                                                                 \
        }                                                                     \
                                                                              \
        auto s_log =                                                          \
            ::stx::scat(::stx::getDateTimeText(::stx::DateTimeFmt::K_ISO_MS), \
                        " [" #level "]["sv,                                   \
                        ::stx::getOsTid(),                                    \
                        "]["sv,                                               \
                        ::stx::getOsThreadTag(),                              \
                        "] "sv,                                               \
                        ADD_SRC_LOC(__VA_ARGS__));                            \
                                                                              \
        if constexpr ("ERROR"sv == #level)                                    \
        {                                                                     \
            ::stx::gLastError().set(s_log);                                   \
        }                                                                     \
                                                                              \
        if (::stx::gCopyLogsToTerminal())                                     \
        {                                                                     \
            if constexpr ("ERROR"sv == #level)                                \
            {                                                                 \
                ::std::cout << ::stx::Tco::K_RED << s_log                     \
                            << ::stx::Tco::K_RESET << ::std::endl;            \
            }                                                                 \
            else if constexpr ("WARN"sv == #level)                            \
            {                                                                 \
                ::std::cout << ::stx::Tco::K_YELLOW << s_log                  \
                            << ::stx::Tco::K_RESET << ::std::endl;            \
            }                                                                 \
            else                                                              \
            {                                                                 \
                ::std::cout << s_log << ::std::endl;                          \
            }                                                                 \
        }                                                                     \
                                                                              \
        ::stx::gLogs().enqueue(std::move(s_log));                             \
    } while (false)

#define LOG_TRACE(...) LOG__(TRACE __VA_OPT__(, ) __VA_ARGS__)
#define LOG_INFO(...)  LOG__(INFO __VA_OPT__(, ) __VA_ARGS__)
#define LOG_WARN(...)  LOG__(WARN __VA_OPT__(, ) __VA_ARGS__)
#define LOG_ERROR(...) LOG__(ERROR __VA_OPT__(, ) __VA_ARGS__)

#define _I_ "|"_I

#define M_SYS_ERR errno, _I_, ::std::strerror(errno)

#define ADD_SRC_LOC(...)                              \
    ::stx::scat(__VA_ARGS__ __VA_OPT__(, ) " ["sv,    \
                ::std::string_view(PRETTY_FUNC_NAME), \
                "] "sv,                               \
                __FILE__,                             \
                ":"sv,                                \
                __LINE__)

#define RUNTIME_ERROR(...) \
    throw ::std::runtime_error(ADD_SRC_LOC(__VA_ARGS__).data())

#define CHECK(...) ::stx::check__(ADD_SRC_LOC(#__VA_ARGS__##sv), __VA_ARGS__)

#define COMPILE_DATE_TIME() ::stx::getCompileDateTime__(__DATE__, __TIME__)

/*
Conceptually, a pure function should be cheap to copy.

A callable may be a heavy function object. So, always pass by raw type and 
std::move(fn). 

An optional object is not as cheap as a pointer. Always std::move(opt) if 
possiple.

Cheap Types:
-----------
- Fundamental types
- std::string_view-like
- std::span-like
- ranges::view-like
- std::shared_ptr-like
- IsNumberLike
- IsDurationLike
- std::time_point
- iterator-like
- Ip4
- Port

void f(T v);
------------
1. T is a cheap type.
2. v may be used as a modifyable local variable.
3. The caller can move/copy an object to v. e.g. std::string.
4. v is a move-only or ref-counted object, e.g. std::function, std::unique_ptr, 
   std::shared_ptr, std::coroutine_handle.

void f(T const v);
------------------
1. T is a cheap type and v shouldn't be modified.
2. The caller can move/copy an object to v. e.g. std::string, and v shouldn't       
   be modified.   
3. v is a move-only or ref-counted object and shouldn't be modified.   

void f(T& v);
-------------
The caller wants v to be modified.

void f(T const& v);
-------------------
The caller wants v to be read only.

void f(T&& v);
--------------
Never use this form except for overload resolution.

The way passing std::optional<T> is the same as passing T.

std::function cannot store a lambda if it contains a move-only captured object.

A note of views::cache1
--------------------------------
Without views::cache1, the following code is illegal. Because views::join 
requires the input range is of a reference-like type. views::cache1 is of a 
reference-like type.
--------------------------------
auto rng = views::iota(0,4)
        | views::transform([](int i) { return std::string(i, char('a' + i)); })
        | views::cache1
        | views::join('-');

auto const il = {'-', 'b', '-', 'c', 'c', '-', 'd', 'd', 'd'};
ranges::equal(rng, il);

static_assert(input_range<decltype(rng)>);
static_assert(!range<const decltype(rng)>);
static_assert(!forward_range<decltype(rng)>);
static_assert(!common_range<decltype(rng)>);

auto rng = "hello"sv | views::trim(::isspace);
*/